import java.util.Random;


public class NeuralNetGenome extends BackPropANN implements Comparable<NeuralNetGenome>, IGenome<NeuralNetGenome>
{
	private static final Random rnd = new Random();
	private double mutationRate = 0.1;
	private double mutationChange = 0.3;
	
	
	private double[][] trainningInputs;
	private double[][] trainningOutputs;
	public NeuralNetGenome(double[][] trainningInputs, double[][] trainningOutputs)
	{
		this.trainningInputs = trainningInputs;
		this.trainningOutputs = trainningOutputs;
		initWeights();
	}
	public double fitnessFunction()
	{
		double fitness = 0.0;
		for(int i=0;i<trainningInputs.length;i++){
			double[] outputs = feedForward(trainningInputs[i]);
			for(int j=0;j<outputs.length;j++){
				fitness += Math.abs(trainningOutputs[i][j]-outputs[j]);
			}
		}
		return fitness;
	}

	public void mutate(){
		int i_h_size = (1+INPUTS)*HIDDEN;
		int h_o_size = (1+HIDDEN)*OUTPUT;
		for(int i=0;i<i_h_size+h_o_size;i++){
			if(rnd.nextDouble()<mutationRate){
				if(i<i_h_size){
					input2HiddenWts[i/(HIDDEN)][i%(HIDDEN)] += rnd.nextDouble()*((mutationChange*2)-mutationChange); 
				}else{
					hidden2OutputWts[(i-i_h_size)/(OUTPUT)][(i-i_h_size)%(OUTPUT)] += rnd.nextDouble()*((mutationChange*2)-mutationChange); 
				}
			}
		}
	}
	public NeuralNetGenome breed(NeuralNetGenome other){
		NeuralNetGenome child = new NeuralNetGenome(trainningInputs, trainningOutputs);
		int i_h_size = (1+INPUTS)*HIDDEN;
		int h_o_size = (1+HIDDEN)*OUTPUT;
		int cutOff = rnd.nextInt(i_h_size+h_o_size);
		NeuralNetGenome parent = this;
		for(int i=0;i<i_h_size+h_o_size;i++){
			if(i==cutOff) parent = other;
			if(i<i_h_size){
				child.input2HiddenWts[i/(HIDDEN)][i%(HIDDEN)] = parent.input2HiddenWts[i/(HIDDEN)][i%(HIDDEN)]; 
			}else{
				child.hidden2OutputWts[(i-i_h_size)/(OUTPUT)][(i-i_h_size)%(OUTPUT)] = parent.hidden2OutputWts[(i-i_h_size)/(OUTPUT)][(i-i_h_size)%(OUTPUT)]; 
			}
		}
	
		return child;
	}
	public int compareTo(NeuralNetGenome o)
	{
		return (int)(fitnessFunction()-o.fitnessFunction());
	}
	public String toString(){
		return ""+fitnessFunction();
	}
}
